Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite emailservice in ruby #109

Merged
merged 6 commits into from
Jun 6, 2022
Merged

Rewrite emailservice in ruby #109

merged 6 commits into from
Jun 6, 2022

Conversation

ahayworth
Copy link
Contributor

Changes

This PR rewrites the emailservice in Ruby. We make the following notable changes:

  • This is an http service, since Ruby and GRPC are not the best of friends. We modify the calling checkoutservice accordingly.
  • I pinned us to otelcol 0.52.0 - whatever version I had locally on my M1 Mac was segfaulting, and pulling the latest version fixed it. This necessitated a small change to the config, since some keys shuffled around in the jaeger exporter section.
  • I changed some of the relevant environmental variables to reflect the HTTP nature of this service, and to properly configure Ruby's OTLP/HTTP exporter; but I tried to change those in a non-invasive way for any other service.

We were able to do this pretty easily, especially since the GRPC request proto for emailservice was very simple and readily serialized to JSON.

There are two potential things wrong with it:

  • I'm pretty sure that the formatting of the email message looks slightly different then the original python version intended ... but honestly I don't think anyone ever looked at it before. 🤷
  • We render the email body into the log, which is a tad bit noisy. On the other hand, the logs are very noisy as-is.

Fixes #37

ahayworth added 4 commits June 4, 2022 00:28
Rather than use GRPC, we reimplement the emailservice as a simple HTTP
application, using Sinatra and OpenTelemetry auto-instrumentation. We
can do this because our use of GRPC in this instance is not very
complicated - we can just serialize the protobuf request to JSON and
send it over HTTP instead, and it's pretty straightforward.

Probably the most interesting part here is that we're rendering the
email text right into the logfile, which might be noisy. We can revisit
that decision if needed.
This commit causes the checkoutservice to call emailservice via HTTP
POST. We leverage the golang net/http autoinstrumentation to create the
spans and propagate context correctly for us.
Because we rewrote the emailservice as an HTTP service, we need to
modify the `EMAIL_SERVICE_ADDR` to reflect the actual scheme (`http`).
We could just do some more string concatenation in the app, I guess.

We pin to a specific collector version - I was getting weird segfaults
with whatever version of the collector was already on my machine, and
they were resolved with 0.52.0. However, that also required a minor
config change to the jaeger exporter. Yay, M1 macs!

We can't specify the otlp/grpc port for the ruby libraries, it won't
work. So for email service we pass the correct OTLP/HTTP URL in the env
var.
@ahayworth ahayworth requested a review from a team June 4, 2022 05:41
@ahayworth
Copy link
Contributor Author

Obligatory screenshot!

Ruby spans in checkout trace

ahayworth added 2 commits June 4, 2022 00:49
Whoops, this was accidentally left in from my testing.
@julianocosta89
Copy link
Member

Hello @ahayworth, why is gRPC not best friends with Ruby?
There are no limitations listed here: https://grpc.io/docs/languages/ruby/

The PR looks good to me, but before approving this PR I'd like to hear the opinion from @cartersocha and @austinlparker.

I personally do not care if it is gRPC or HTTP, but as far as I remember we would keep gRPC wherever possible in the sample.

@ahayworth
Copy link
Contributor Author

Hello @ahayworth, why is gRPC not best friends with Ruby? There are no limitations listed here: https://grpc.io/docs/languages/ruby/

Lack of limitations does not imply that it works well, sadly. I wish they were better friends.

Technical things:

Socio-technical things:

  • There has been very little activity in the Ruby GRPC library in recent years, once you exclude autogenerated C-extension changes. In fact, in src/ruby/lib/grpc/generic (where the high-level bits are, before you drop down to the C extension) there has only been 1 commit in the past year.
  • It kinda seems like the folks over at the GRPC project aren't interested in providing first-class support for Ruby. Others have noted the same, but even just benchmarking shows that it's pretty awful (granted, python doesn't look too great there either).
  • Those same C-extensions mean that GRPC flat-out doesn't work with jruby, which is still an important platform for Ruby. This alone holds back broader adoption in the broader Ruby community.
  • The "Rails effect" means that just most things in the Ruby ecosystem tend to revolve around Rails - and in turn, that means they tend to be focused on HTTP.

OpenTelemetry-specific things:

  • We were recently asked in the Ruby SIG if we could implement GRPC as a required OTLP transport mechanism. The comment here speaks to the issues with Ruby and GRPC in general, and was part of why OTLP/HTTP is actually the required OTLP transport now for SDKs.

No small amount of these issues are related / intertwined with protobuf, too - it's also an issue on Ruby. I've been writing this comment for quite awhile now so I won't dig up as much for discussion, but here's a few things:


All of this aside: it's definitely possible to use GRPC with Ruby. But it's generally a poor experience, and I've personally been annoyed every time I try to do it.

There are a lot of outstanding technical issues with both GRPC and protobuf, and the ruby wrappers don't see a lot of love from the maintainers. There are frequently issues with new versions, and my sense is that "nobody on these projects is testing these releases with Ruby" - just the sheer number of "we can't install the new version on ruby" issues alone demonstrates as much.

Setting that aside, using the grpc gem is just confusing and not ruby-like. Trying to dive in the code is a huge mess, from my experience. It's not impossible, but it feels like a half-baked project for Ruby in a lot of ways. Of course, that's a subjective feeling on my part but I think I've linked enough issues and discussions to at least show I'm not alone. 😆

I really don't know why this is, I guess. My own personal thought is that it's a bad idea to just implement client libraries as thin wrappers around C extensions without really trying to implement things in a language-first fashion.


Anyways: is it impossible to do this as a GRPC service? No, but I'd really prefer not to. Setting aside the general annoyances, it's just not a good demo for Ruby. The community is heavily entrenched in HTTP, and that's what is shown off the best. We don't even have GRPC instrumentation - and precious few people have even asked. So even if we did this in GRPC, it's just not going to be as nice as an HTTP version.

Copy link
Contributor

@mic-max mic-max left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

src/emailservice/Dockerfile Show resolved Hide resolved
@julianocosta89
Copy link
Member

Awesome @ahayworth!
Thank you very much for the explanation.

Discussed with everyone at the SIG meeting and this is good to go! 🚀

@cartersocha cartersocha merged commit bc0cd67 into open-telemetry:main Jun 6, 2022
cartersocha pushed a commit that referenced this pull request Jun 6, 2022
If I had invested, say, a mere modicum of effort into understanding what
needed to be done to use a `-slim` image in the first place, then we
would have included this in #109 from the start. However, I clearly did not. 🤦

This change switches us to the `-slim` image, and installs the one thing
we actually need to build `puma`: Debian's `build-essential` package.
require "opentelemetry/instrumentation/sinatra"

OpenTelemetry::SDK.configure do |c|
c.use "OpenTelemetry::Instrumentation::Sinatra"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think c.use_all is the use that we want to steer folks towards.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good point.

end

post "/send_order_confirmation" do
data = JSON.parse(request.body.read, object_class: OpenStruct)
Copy link

@arielvalentin arielvalentin Jun 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any change we can use a Hash, Struct or Object here instead to mitigate memory leaks?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to open a pr with the proposed changes. I know @ahayworth would love the help!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any change we can use a Hash, Struct or Object here instead to mitigate memory leaks?

This was the fastest path to victory without mucking up the already existing email template. But, in general: yes, we absolutely could!


require "opentelemetry/sdk"
require "opentelemetry/exporter/otlp"
require "opentelemetry/instrumentation/sinatra"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use Bundler here to load dependencies and use Bundler.require instead of requiring each dependency separately?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe so, yes.

GaryPWhite pushed a commit to wayfair-contribs/opentelemetry-demo that referenced this pull request Jun 30, 2022
* feat: rewrite emailservice in ruby

Rather than use GRPC, we reimplement the emailservice as a simple HTTP
application, using Sinatra and OpenTelemetry auto-instrumentation. We
can do this because our use of GRPC in this instance is not very
complicated - we can just serialize the protobuf request to JSON and
send it over HTTP instead, and it's pretty straightforward.

Probably the most interesting part here is that we're rendering the
email text right into the logfile, which might be noisy. We can revisit
that decision if needed.

* feat: call emailservice via HTTP POST

This commit causes the checkoutservice to call emailservice via HTTP
POST. We leverage the golang net/http autoinstrumentation to create the
spans and propagate context correctly for us.

* feat: modify setup to work with otel/ruby

Because we rewrote the emailservice as an HTTP service, we need to
modify the `EMAIL_SERVICE_ADDR` to reflect the actual scheme (`http`).
We could just do some more string concatenation in the app, I guess.

We pin to a specific collector version - I was getting weird segfaults
with whatever version of the collector was already on my machine, and
they were resolved with 0.52.0. However, that also required a minor
config change to the jaeger exporter. Yay, M1 macs!

We can't specify the otlp/grpc port for the ruby libraries, it won't
work. So for email service we pass the correct OTLP/HTTP URL in the env
var.

* docs: note that emailservice is now ruby

* fixup: remove `loglevel: debug` from otel-col config

Whoops, this was accidentally left in from my testing.

* fixup: appease markdownlint
GaryPWhite pushed a commit to wayfair-contribs/opentelemetry-demo that referenced this pull request Jun 30, 2022
If I had invested, say, a mere modicum of effort into understanding what
needed to be done to use a `-slim` image in the first place, then we
would have included this in open-telemetry#109 from the start. However, I clearly did not. 🤦

This change switches us to the `-slim` image, and installs the one thing
we actually need to build `puma`: Debian's `build-essential` package.
jmichalak9 pushed a commit to jmichalak9/opentelemetry-demo that referenced this pull request Mar 22, 2024
* feat: rewrite emailservice in ruby

Rather than use GRPC, we reimplement the emailservice as a simple HTTP
application, using Sinatra and OpenTelemetry auto-instrumentation. We
can do this because our use of GRPC in this instance is not very
complicated - we can just serialize the protobuf request to JSON and
send it over HTTP instead, and it's pretty straightforward.

Probably the most interesting part here is that we're rendering the
email text right into the logfile, which might be noisy. We can revisit
that decision if needed.

* feat: call emailservice via HTTP POST

This commit causes the checkoutservice to call emailservice via HTTP
POST. We leverage the golang net/http autoinstrumentation to create the
spans and propagate context correctly for us.

* feat: modify setup to work with otel/ruby

Because we rewrote the emailservice as an HTTP service, we need to
modify the `EMAIL_SERVICE_ADDR` to reflect the actual scheme (`http`).
We could just do some more string concatenation in the app, I guess.

We pin to a specific collector version - I was getting weird segfaults
with whatever version of the collector was already on my machine, and
they were resolved with 0.52.0. However, that also required a minor
config change to the jaeger exporter. Yay, M1 macs!

We can't specify the otlp/grpc port for the ruby libraries, it won't
work. So for email service we pass the correct OTLP/HTTP URL in the env
var.

* docs: note that emailservice is now ruby

* fixup: remove `loglevel: debug` from otel-col config

Whoops, this was accidentally left in from my testing.

* fixup: appease markdownlint
jmichalak9 pushed a commit to jmichalak9/opentelemetry-demo that referenced this pull request Mar 22, 2024
If I had invested, say, a mere modicum of effort into understanding what
needed to be done to use a `-slim` image in the first place, then we
would have included this in open-telemetry#109 from the start. However, I clearly did not. 🤦

This change switches us to the `-slim` image, and installs the one thing
we actually need to build `puma`: Debian's `build-essential` package.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Reimplement 'emailservice' in Ruby
5 participants